package jmine.tec.rpc.server.representation.representers.reflection;

import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;

import jmine.tec.rpc.common.annotation.Lazy;
import jmine.tec.utils.reflection.GenericTypeResolverImpl;

/**
 * Classe utilitária para representers, ajuda a lidar, principalmente, com reflecção e acesso à propriedades
 * 
 * @author lundberg
 */
public final class RepresenterUtil {

    private static final Map<Class<?>, Class<?>> PRIMITIVE_COERCION = new HashMap<Class<?>, Class<?>>();
    static {
        PRIMITIVE_COERCION.put(double.class, Double.class);
        PRIMITIVE_COERCION.put(int.class, Integer.class);
        PRIMITIVE_COERCION.put(short.class, Short.class);
        PRIMITIVE_COERCION.put(boolean.class, Boolean.class);
        PRIMITIVE_COERCION.put(char.class, Character.class);
        PRIMITIVE_COERCION.put(long.class, Long.class);
        PRIMITIVE_COERCION.put(byte.class, Byte.class);
        PRIMITIVE_COERCION.put(float.class, Float.class);
    }

    /**
     * Construtor
     */
    private RepresenterUtil() {
        // OK
    }

    /**
     * @param descriptor descriptor
     * @return boolean
     */
    public static boolean isBeanProperty(PropertyDescriptor descriptor) {
        return descriptor.getWriteMethod() != null && descriptor.getReadMethod() != null;
    }

    /**
     * Obtém o tipo genérico do tipo parametrizável com o índice dado
     * 
     * @param property property
     * @param index index
     * @return Class
     * @deprecated preferir {@link #getParameterizedPropertyType(PropertyDescriptor, int, Class)}
     */
    public static Class<?> getParameterizedTypeArgument(PropertyDescriptor property, int index) {
        return getParameterizedPropertyType(property, index, property.getReadMethod().getDeclaringClass());
    }

    /**
     * Obtém o tipo genérico do tipo parametrizável com o índice dado
     * 
     * @param property property
     * @param index index
     * @param declaringClass a classe que declara a propriedade
     * @return Class a classe concreta
     */
    public static Class<?> getParameterizedPropertyType(PropertyDescriptor property, int index, Class<?> declaringClass) {
        return GenericTypeResolverImpl.getInstance().resolveTypeForParameterizedType(
                (ParameterizedType) property.getReadMethod().getGenericReturnType(), declaringClass, index);
    }

    /**
     * Verifica se as propriedades possuem o tipo de retorno dado.
     * 
     * @param returnType returnType
     * @param descriptors descriptors
     * @return boolean
     */
    public static boolean classesMatch(Class<?> returnType, PropertyDescriptor... descriptors) {
        for (PropertyDescriptor descriptor : descriptors) {
            if (!returnType.equals(getPropertyType(descriptor))) {
                return false;
            }
        }
        return true;
    }

    /**
     * Retorna classe do descritor, trazendo sempre classes wrapper correspondentes, no caso de primitivos
     * 
     * @param property property
     * @return Class
     */
    public static Class<?> getPropertyType(PropertyDescriptor property) {
        if (property.getPropertyType().isPrimitive()) {
            return PRIMITIVE_COERCION.get(property.getPropertyType());
        } else {
            return property.getPropertyType();
        }
    }

    /**
     * Verifica se a propriedade é lazy.
     * 
     * @param representationProperty representationProperty
     * @return boolean
     */
    public static boolean isLazy(PropertyDescriptor representationProperty) {
        return representationProperty.getReadMethod().isAnnotationPresent(Lazy.class);
    }
}
